import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_lui/flutter_lui.dart';
import 'package:flutter_lui/src/l_style.dart';

/// toast显示时移动的偏移量
final _moveOffset = lrpx(30);

/// toast显示时移动的持续时间
const _moveDuration = 200;

enum LToastPosition {
  top,
  up,
  center,
  down,
  bottom,
}

class LToast {
  /// 默认字体大小
  static double _fontSize = LStyle.fontSize;

  /// 默认显示位置
  static LToastPosition _position = LToastPosition.down;

  /// 默认显示时长
  static int _duration = 3;

  /// 计数器 相当于一个唯一标识 用于消失动画完成后移除overlayEntry
  static int _count = 1;

  /// 以计数器为Key的所有overlayEntry 集合 关闭时以key移除对应的overlayEntry
  static final Map<int, OverlayEntry?> _entries = {};

  /// 记录不跨页面的toast是在哪个路由下展示的, 当路由有变化时,根据key移除所有路由不一致的toast
  static final Map<int, Route<dynamic>?> _routeMap = {};

  static init({
    double? fontSize,
    LToastPosition? position,
    int? duration,
  }) {
    if (fontSize != null) {
      _fontSize = fontSize;
    }
    if (position != null) {
      _position = position;
    }
    if (duration != null) {
      _duration = duration;
    }
  }

  static hide(int key) {
    if (_entries[key] != null) {
      _entries[key]!.remove();
      _entries.remove(key);
      if (_routeMap.containsKey(key) && _routeMap[key] != null) {
        _routeMap.remove(key);
      }
    }
  }

  static handleRouteUpdate(List<Route<dynamic>?> routes) {
    if (_routeMap.keys.isEmpty) return;
    for (int key in _routeMap.keys) {
      if (_routeMap[key] != LNavigatorObserver.currentRoute) {
        hide(key);
        _routeMap.remove(key);
      }
    }
  }

  static addListener() {
    if (!LEvents.instance
        .hasListener(LNavigatorObserver.ROUTE_DID_UPDATE, handleRouteUpdate)) {
      LEvents.instance
          .addListener(LNavigatorObserver.ROUTE_DID_UPDATE, handleRouteUpdate);
    }
  }

  static show(
      {String? message,
      String? title,
      int? duration,
      LToastPosition? position,
      double? titleFontSize,
      double? messageFontSize,
      LUIType? type,
      bool? crossPage = true,
      Widget? child}) {
    addListener();
    var useMessageFontSize = messageFontSize ?? LToast._fontSize;
    var useTitleFontSize = titleFontSize ?? (useMessageFontSize - 2);

    var useChild = child != null
        ? ConstrainedBox(
            constraints: BoxConstraints(maxWidth: PageSize.width - lrpx(40)),
            child: child)
        : LBox(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              title?.isNotEmpty == true
                  ? LText(title!,
                      fontSize: useTitleFontSize,
                      color: Colors.white,
                      marginBottom: rpx(5))
                  : Container(),
              ConstrainedBox(
                  constraints:
                      BoxConstraints(maxWidth: PageSize.width - lrpx(40)),
                  child: LText(
                    message ?? '',
                    fontSize: useMessageFontSize,
                    color: Colors.white,
                  ))
            ],
          );
    LToast._count += 1;
    var currCount = LToast._count;
    var overlayEntry = OverlayEntry(
        builder: (_) => _LToastChild(
              position: position ?? LToast._position,
              type: type,
              onClose: () => hide(currCount),
              child: useChild,
            ));
    LToast._entries[currCount] = overlayEntry;
    if (crossPage != true) {
      _routeMap[currCount] = LNavigatorObserver.currentRoute;
    }
    if (LNavigatorObserver.currentRoute?.navigator?.overlay != null) {
      LNavigatorObserver.currentRoute?.navigator!.overlay!.insert(overlayEntry);
    }
  }
}

class _LToastChild extends StatefulWidget {
  final Widget child;
  final LToastPosition position;
  final LUIType? type;
  final Function() onClose;
  const _LToastChild(
      {required this.child,
      required this.position,
      required this.onClose,
      this.type,
      Key? key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => __LToastChild();
}

class __LToastChild extends State<_LToastChild> with TickerProviderStateMixin {
  late AnimationController moveController;
  late Animation<double> moveAnimation;
  late AnimationController opacityController;
  late Animation<double> opacityAnimation;

  double useTop = 0;
  bool isTop = true;
  @override
  void initState() {
    super.initState();
    useTop = 0.0;
    isTop = widget.position == LToastPosition.up ||
        widget.position == LToastPosition.top;
    if (widget.position == LToastPosition.up ||
        widget.position == LToastPosition.down) {
      useTop = PageSize.height / 10;
    } else if (widget.position == LToastPosition.center) {
      useTop = PageSize.height / 2;
    }

    moveController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: _moveDuration));

    moveAnimation = Tween<double>(
      begin: useTop - _moveOffset,
      end: useTop,
    ).animate(CurvedAnimation(parent: moveController, curve: Curves.ease));

    moveAnimation.addListener(() {
      setState(() {
        useTop = moveAnimation.value;
      });
    });

    opacityController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: _moveDuration));
    opacityAnimation = Tween<double>(
      begin: 0.0,
      end: 1.0,
    ).animate(CurvedAnimation(parent: opacityController, curve: Curves.ease));
    moveController.forward();
    opacityController.forward();
    startTimer();
  }

  @override
  void dispose() {
    moveController.dispose();
    opacityController.dispose();
    super.dispose();
  }

  void startTimer() {
    Timer(Duration(seconds: LToast._duration), () async {
      if (!mounted) return;
      moveController.reverse();
      await opacityController.reverse();
      widget.onClose();
    });
  }

  @override
  Widget build(BuildContext context) {
    var useBackgroundColor = widget.type == LUIType.primary
        ? LUI.instance.primaryColor
        : widget.type == LUIType.success
            ? LUI.instance.successColor
            : widget.type == LUIType.warning
                ? LUI.instance.warningColor
                : widget.type == LUIType.danger
                    ? LUI.instance.dangerColor
                    : Colors.black.withOpacity(0.8);
    return Positioned(
      left: 0,
      top: isTop ? useTop : null,
      bottom: isTop ? null : useTop,
      child: Material(
        color: Colors.transparent,
        child: FadeTransition(
          opacity: opacityAnimation,
          child: LBox(
              padding: widget.position == LToastPosition.top
                  ? [PageSize.statusBarHeight, 0, 0, 0]
                  : widget.position == LToastPosition.bottom
                      ? [0, 0, PageSize.safeAreaHeight]
                      : [0],
              backgroundColor: (widget.position == LToastPosition.top ||
                      widget.position == LToastPosition.bottom)
                  ? useBackgroundColor
                  : null,
              width: PageSize.width,
              direction: BoxDirections.horizontal,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                LBox(
                  padding: [lrpx(5), lrpx(10)],
                  borderRadius: [LStyle.borderRadius],
                  direction: BoxDirections.horizontal,
                  backgroundColor: (widget.position != LToastPosition.top &&
                          widget.position != LToastPosition.bottom)
                      ? useBackgroundColor
                      : null,
                  children: [
                    widget.child,
                  ],
                ),
              ]),
        ),
      ),
    );
  }
}
